#include "GridTIFF.hpp"

#include"gdal/gdal.h"
#include"gdal/gdal_priv.h"

#include <iostream>
#include<string>

GridTIFF::GridTIFF()
{

}

GridTIFF::~GridTIFF()
{
    if (this->values != nullptr) {
        // 逐个释放每个 band 的数据
        for (int i = 0; i < this->_bandCount; ++i) {
            if (this->values[i] != nullptr) {
                CPLFree(this->values[i]); // 使用 CPLFree 释放 CPLMalloc 分配的内存
                this->values[i] = nullptr;
            }
        }
        // 释放指针数组本身
        delete[] this->values;
        this->values = nullptr;
    }
}



/// <summary>
/// 保存为GridTIFF文件
/// </summary>
bool GridTIFF::saveToFile(const char *fileName)
{
    //注册所有的驱动
    CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
    GDALAllRegister();

    //GDAL的driver
    const char* pszFormat = "GTiff";
    GDALDriver* poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat);
    if (poDriver == NULL)
    {
        return false;
    }

    auto gdalDataType = static_cast<GDALDataType>(this->_dataType);
    GDALDataset* poDstDS = poDriver->Create(fileName, this->_size*2, this->_size*2, this->_bandCount, gdalDataType, NULL);

    for (int bandId = 0; bandId < this->_bandCount; bandId++)
    {
        poDstDS->GetRasterBand(bandId + 1)->SetNoDataValue(this->_noValue);
        if( this->values[bandId]!= nullptr)
            poDstDS->GetRasterBand(bandId + 1)->RasterIO(GF_Write, 0, 0, this->_size*2, this->_size*2, this->values[bandId], this->_size*2, this->_size*2, gdalDataType, 0, 0);
    }

    // 设置文件类型
    poDstDS->SetMetadataItem("fileType", "NNU_GRID_TIFF");

    //设置转换参数
    poDstDS->SetMetadataItem("level", std::to_string(this->_trans.level).c_str());
    poDstDS->SetMetadataItem("basePartition", std::to_string(this->_trans.basePartition).c_str());
    poDstDS->SetMetadataItem("rowOrigin", std::to_string(this->_trans.rowOrigin).c_str());
    poDstDS->SetMetadataItem("colOrigin", std::to_string(this->_trans.colOrigin).c_str());

    poDstDS->SetMetadataItem("elementType", enum2String(this->_elementType).c_str());
    poDstDS->SetMetadataItem("gridType", enum2String(this->_gridType).c_str());

    GDALClose((GDALDatasetH)poDstDS);
    return true;
}

/// <summary>
/// 读取GridTIFF文件
/// </summary>
bool GridTIFF::loadFromFile(const char* fileName)
{
    //注册所有的驱动
    CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
    GDALAllRegister();

    //GDAL的driver
    auto poSrcDS = (GDALDataset*)GDALOpen(fileName, GA_ReadOnly);
    if (poSrcDS == NULL)
        return false;

    //判断是否为GridTIFF
    const char *fileType = poSrcDS->GetMetadataItem("fileType");
    if (fileType == nullptr)
    {
        std::cout << "he file is not GridTIFF" << std::endl;
        return false;
    }

    if(std::string(fileType)!="NNU_GRID_TIFF")
        return false;

    int xSize = poSrcDS->GetRasterXSize();
    int ySize = poSrcDS->GetRasterYSize();

    if(xSize!=ySize)
    {
        std::cerr<<"the file is not GridTIFF"<<std::endl;
        return false;
    }

    this->_size= xSize/2;

    this->_trans.basePartition= atoi(poSrcDS->GetMetadataItem("basePartition"));
    this->_trans.level=atoi(poSrcDS->GetMetadataItem("level"));
    this->_trans.rowOrigin=atoi(poSrcDS->GetMetadataItem("rowOrigin"));
    this->_trans.colOrigin=atoi(poSrcDS->GetMetadataItem("colOrigin"));

    this->_elementType=getElementTypeFromStr(poSrcDS->GetMetadataItem("elementType"));
    this->_gridType=getGridTypeFromStr(poSrcDS->GetMetadataItem("gridType"));


    this->_bandCount= poSrcDS->GetRasterCount();
    GDALDataType gdalDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    this->_dataType = static_cast<DGGSDataType>(gdalDataType);
    this->_noValue=poSrcDS->GetRasterBand(1)->GetNoDataValue();

    delete this->values;
    this->values = new void*[this->_bandCount];
    for (int bandId = 0; bandId < this->_bandCount; bandId++)
    {
        // 分配数据空间
        size_t dataSize = GDALGetDataTypeSizeBytes(gdalDataType) * xSize * ySize;
        this->values[bandId] = CPLMalloc(dataSize);
        poSrcDS->GetRasterBand(bandId + 1)->RasterIO(GF_Read, 0, 0, xSize, ySize, this->values[bandId],  xSize, ySize, gdalDataType, 0, 0);
    }

    GDALClose((GDALDatasetH)poSrcDS);
    return true;
}


/// <summary>
/// 读取GridTIFF文件的参数信息
/// </summary>
bool GridTIFF::getParams(const char *fileName)
{
    //注册所有的驱动
    CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
    GDALAllRegister();

    //GDAL的driver
    auto poSrcDS = (GDALDataset*)GDALOpen(fileName, GA_ReadOnly);
    if (poSrcDS == NULL)
        return false;

    int xSize = poSrcDS->GetRasterXSize();
    int ySize = poSrcDS->GetRasterYSize();

    if(xSize!=ySize)
    {
        std::cerr<<"the file is not GridTIFF"<<std::endl;
        return false;
    }

    this->_size= xSize/2;
    this->_bandCount= poSrcDS->GetRasterCount();
    GDALDataType gdalDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    this->_dataType = static_cast<DGGSDataType>(gdalDataType);
    this->_noValue=poSrcDS->GetRasterBand(1)->GetNoDataValue();

    this->_trans.basePartition= atoi(poSrcDS->GetMetadataItem("basePartition"));
    this->_trans.level=atoi(poSrcDS->GetMetadataItem("level"));
    this->_trans.rowOrigin=atoi(poSrcDS->GetMetadataItem("rowOrigin"));
    this->_trans.colOrigin=atoi(poSrcDS->GetMetadataItem("colOrigin"));




    GDALClose((GDALDatasetH)poSrcDS);
    return true;
}

void GridTIFF::setSize(int size)
{
    this->_size=size;
    if(this->_size!=0&&this->_bandCount!=0)
    {
        size_t dataSize = GDALGetDataTypeSizeBytes(static_cast<GDALDataType>(this->_dataType)) *  this->_size *  this->_size * 4;
        for(int bandId=0;bandId<this->_bandCount;bandId++)
        {
            this->values[bandId]=CPLMalloc(dataSize);
        }
    }
}


void GridTIFF::setBandCount(int bandCount)
{
    this->_bandCount=bandCount;
    if(this->values== nullptr)
        this->values=new void*[bandCount];
    if(this->_size!=0)
    {
        size_t dataSize = GDALGetDataTypeSizeBytes(static_cast<GDALDataType>(this->_dataType)) *  this->_size *  this->_size * 4;
        for(int bandId=0;bandId<this->_bandCount;bandId++)
        {
            this->values[bandId]=CPLMalloc(dataSize);
        }
    }
}

bool code2XY(DGGSElementCode code, int& x, int& y, DGGridBBox gridBox)
{
    if (code.basePartition != gridBox.basePartition)
    {
        //表示没找到，可以跳过
        x = -1, y = -1;
        return false;
    }

    long long int colMin = gridBox.colMax - gridBox.colSize + 1;
    long long int rowMin = gridBox.rowMin;
    int x0 = 0;
    int y0 = (int)gridBox.rowSize - 1;

    x = int(code.row - rowMin + code.col - colMin + x0);
    y = int((code.col - colMin) - (code.row - rowMin) + y0);
    if (y < 0 || x < 0)
    {
        //表示没找到，可以跳过
        x = -1, y = -1;
        return false;
    }
    return true;
}

DGGridBBox getGridBoxFromGridTran(GridTransform gridTrans,int gridSize)
{
    DGGridBBox gridBBox;
    gridBBox.basePartition=gridTrans.basePartition;
    gridBBox.colSize=gridSize;
    gridBBox.rowSize=gridSize;
    gridBBox.rowMin=gridTrans.rowOrigin;
    gridBBox.colMax=gridTrans.colOrigin+gridBBox.colSize-1;

    return gridBBox;
}
bool GridTIFF::setValue(DGGSElementCode code,int bandId, double value)
{
    int x,y;
    code2XY(code,x,y,gridBBox);

    if(x==-1||y==-1)
        return false;

    int xSize=(int) gridBBox.rowSize*2;
    int ySize=(int) gridBBox.colSize*2;


#define SET_VALUE(DATA_TYPE,DGGS_TYPE) \
    case DGGS_TYPE:                    \
    {                              \
        DATA_TYPE *p=(DATA_TYPE *)this->values[bandId];\
        p[(static_cast<long long>(ySize) - 2 - y) * xSize + x] = static_cast<DATA_TYPE>(value);\
        p[(static_cast<long long>(ySize) - 2 - y) * xSize + x + 1] =static_cast<DATA_TYPE>(value); \
        break;\
    }

    switch (this->_dataType) {
        SET_VALUE(unsigned char, DGGS_Byte)
        SET_VALUE(unsigned short, DGGS_UInt16)
        SET_VALUE(short, DGGS_Int16)
        SET_VALUE(unsigned int, DGGS_UInt32)
        SET_VALUE(int, DGGS_Int32)
        SET_VALUE(float, DGGS_Float32)
        SET_VALUE(double, DGGS_Float64)
        default:
            break;
    }
    return true;
}

double GridTIFF::getValue(DGGSElementCode code,int bandId)
{

    double value = noData;

    int x,y;
    code2XY(code,x,y,gridBBox);

    if(x==-1||y==-1)
        return value;

    int xSize=(int) gridBBox.rowSize*2;
    int ySize=(int) gridBBox.colSize*2;


#define SET_VALUE(DATA_TYPE,DGGS_TYPE) \
    case DGGS_TYPE:                    \
    {                              \
        DATA_TYPE *p=(DATA_TYPE *)this->values[bandId];\
        value== (p[(static_cast<long long>(ySize) - 2 - y) * xSize + x] );\
        break;\
    }

    switch (this->_dataType) {
        SET_VALUE(unsigned char, DGGS_Byte)
        SET_VALUE(unsigned short, DGGS_UInt16)
        SET_VALUE(short, DGGS_Int16)
        SET_VALUE(unsigned int, DGGS_UInt32)
        SET_VALUE(int, DGGS_Int32)
        SET_VALUE(float, DGGS_Float32)
        SET_VALUE(double, DGGS_Float64)
        default:
            break;
    }
    return value;
}

void GridTIFF::setTrans(GridTransform trans)
{
    this->_trans=trans;
    this->gridBBox=getGridBoxFromGridTran(trans,this->_size);

}


bool GridTIFF::Finalize() {

    int xSize=(int) gridBBox.rowSize*2;
    int ySize=(int) gridBBox.colSize*2;

    #define SET_NO_VALUE(DATA_TYPE,DGGS_TYPE) \
        case DGGS_TYPE:                    \
        {                                     \
            for(int bandId=0;bandId<this->_bandCount;bandId++) \
            {                                 \
                DATA_TYPE *p=(DATA_TYPE *)this->values[bandId];\
                for(int i=0;i<xSize*ySize;i++)    \
                    p[i]=static_cast<long long>(this->_noValue); \
            }                                  \
            break;                                    \
        }

    switch (this->_dataType) {
        SET_NO_VALUE(unsigned char, DGGS_Byte)
        SET_NO_VALUE(unsigned short, DGGS_UInt16)
        SET_NO_VALUE(short, DGGS_Int16)
        SET_NO_VALUE(unsigned int, DGGS_UInt32)
        SET_NO_VALUE(int, DGGS_Int32)
        SET_NO_VALUE(float, DGGS_Float32)
        SET_NO_VALUE(double, DGGS_Float64)
        default:
            break;
    }
    return true;
}





